"""
Grave! Important! Важно!

Proletoj el ĉiuj landoj, unuiĝu!
Workers of the world, unite!
Пролетарии всех стран, соединяйтесь!

https://tkom.pro
"""

from django.utils.translation import gettext_lazy as _
from django.db import transaction
from django.utils import timezone
import graphene

from muroj.tasks import email_post_notice, sciigi_uzantojn
from siriuso.api.mixins import SiriusoAuthMutation
from siriuso.utils import set_enhavo, get_lang_kodo, set_priskribo
from siriuso.utils.thumbnailer import get_image_properties
from ..models import *
from .schema import (MuroEnskriboProprietoj, MurojEnskribo, MurojEnskriboKomento, MuroEnskriboNode,
                     MurojUzantoEnskriboNode)


class InteresaEnskribo(graphene.Mutation):
    class Arguments:
        enskribo_uuid = graphene.UUID(required=True)

    status = graphene.Boolean()
    message = graphene.String()
    statistiko = graphene.Field(MuroEnskriboProprietoj)

    @staticmethod
    def __queryset(enskribo_uuid):
        model_dict = {
            MuroEnskribo: MuroEnskriboInterese,
            MurojUzantoEnskribo: MurojUzantoEnskriboInterese
        }

        for socio_model, intereso_model in model_dict.items():
            try:
                enskribo = socio_model.objects.get(uuid=enskribo_uuid)
                return (enskribo, intereso_model)
            except socio_model.DoesNotExist:
                pass

        return (None, None)

    @staticmethod
    def mutate(root, info, enskribo_uuid):
        status = False

        if info.context.user.is_authenticated:
            enskribo, intereso_model = InteresaEnskribo.__queryset(enskribo_uuid)

            if issubclass(intereso_model, MurojUzantoEnskriboInterese):
                autoro = {'posedanto': info.context.user}
            else:
                autoro = {'autoro': info.context.user}

            if enskribo and intereso_model:
                try:
                    intereso = intereso_model.objects.get(enskribo_id=enskribo_uuid, forigo=False,
                                                          **autoro)
                    intereso.forigo = True
                    intereso.foriga_dato = timezone.now()
                    intereso.save()

                    message = _('Пометка "Интересно" снята')
                except intereso_model.DoesNotExist:
                    intereso = intereso_model(enskribo_id=enskribo_uuid, forigo=False, interese=True,
                                              **autoro)
                    intereso.save()

                    message = _('Помечено как "Интересно"')

                status = True
                return InteresaEnskribo(status=status, message=message,
                                        statistiko=MuroEnskriboProprietoj(enskribo=enskribo, info=info))
            return InteresaEnskribo(status=status, message=_('Запись стены не найдена'))
        return InteresaEnskribo(status=status, message=_('Требуется авторизация'))


# Возвращает тип членства и модели записей стены по UUID сообщества
def get_enskribo_model(komunumo_uuid, uzanto):
    models_dict = {
        Komunumo: (KomunumoMembro, Muro, MuroEnskribo, MuroEnskriboBildo, MuroEnskriboKomento),
        Uzanto: (None, MurojUzantoMuro, MurojUzantoEnskribo, MurojUzantoEnskriboBildo, MurojUzantoEnskriboKomento)
    }

    for kom_model, muroj_models in models_dict.items():
        try:
            membro_model = muroj_models[0]
            muro_model = muroj_models[1]

            komunumo = kom_model.objects.get(uuid=komunumo_uuid)
            membro = (membro_model.objects.select_related('tipo')
                      .filter(posedanto=komunumo, autoro=uzanto, forigo=False) if membro_model else None)

            if membro and membro.count():
                membro = membro[0].tipo.kodo
            else:
                membro = None

            muro = muro_model.objects.get(posedanto=komunumo, forigo=False)

            result = [membro, muro]
            result.extend(muroj_models[2:])
            return result
        except (kom_model.DoesNotExist, muroj_models[1].DoesNotExist):
            pass

    return None


class RedaktuMuroEnskribo(SiriusoAuthMutation, graphene.Mutation):
    class Arguments:
        komunumo_uuid = graphene.UUID(required=True)
        enskribo_uuid = graphene.UUID()
        teksto = graphene.String()
        chefa_lingvo = graphene.Boolean()
        priskribo = graphene.String()
        bildo = graphene.String()
        video = graphene.String()
        autoro_montri = graphene.Boolean()
        aliro = graphene.String()
        komentado_aliro = graphene.String()
        publikigo = graphene.Boolean()
        arkivo = graphene.Boolean()
        forigo = graphene.Boolean()
        poshto = graphene.Boolean()
        fiksa = graphene.Boolean()

    status = graphene.Boolean()
    message = graphene.String()
    enskribo = graphene.Field(MurojEnskribo)

    @staticmethod
    def __get_aliro(kodo, uzanto_muro=False):
        try:
            if uzanto_muro:
                return UzantojAliro.objects.get(kodo=kodo, forigo=False)

            return KomunumojAliro.objects.get(kodo=kodo, forigo=False)
        except:
            return None

    @staticmethod
    def mutate(root, info, komunumo_uuid, **kwargs):
        status = False
        message = None

        if info.context.user.is_authenticated:
            enskribo_models = get_enskribo_model(komunumo_uuid, info.context.user)

            if enskribo_models:
                membro, muro, enskribo_model, bildo_model, kom_model = enskribo_models
                model_fields = tuple(field.name for field in enskribo_model._meta.get_fields())

                # Надо будет переделать определение прав, пока права по типу членства
                has_perm = (membro in ('moderiganto', 'administranto', 'komunumano-adm', 'komunumano-mod')
                            if not isinstance(muro, MurojUzantoMuro)
                            else getattr(muro, 'posedanto') == info.context.user)

                aliro = RedaktuMuroEnskribo.__get_aliro('chiuj', uzanto_muro=isinstance(muro, MurojUzantoMuro))
                komentado_aliro = aliro

                # Проверяем права
                if not has_perm:
                    message = _('Недостаточно прав')
                elif 'bildo' in info.context.FILES:
                    # Проверяем формат картинки, если она есть
                    props = get_image_properties(info.context.FILES['bildo'])

                    if props and props.get('format') not in ['JPEG', 'PNG', 'GIF']:
                        message = _('Неверный формат изображения, разрешены: JPEG, GIF, PNG')
                    elif not props:
                        message = _('Файл не является изображением')

                if message:
                    return RedaktuMuroEnskribo(status=status, message=message)

                # По переданным параметрам определяем, что нужно сделать
                # 1. Редактируем сообщение
                if 'enskribo_uuid' in kwargs:
                    params = ['teksto', 'bildo', 'video', 'autoro_montri', 'aliro', 'komentado_aliro', 'publikigo',
                              'arkivo', 'forigo']

                    if not len(set(params).intersection(set(kwargs.keys()))) and 'bildo' not in info.context.FILES:
                        message = _('Не заданы параметры для изменения')

                    with transaction.atomic():
                        try:
                            enskribo = enskribo_model.objects.select_for_update().get(
                                uuid=kwargs.get('enskribo_uuid'),
                            )
                        except enskribo_model.DoesNotExist:
                            enskribo = None
                            message = _('Запись не найдена')

                        lasta_statuso = enskribo.publikigo

                        if enskribo:
                            try:
                                bildo = bildo_model.objects.get(posedanto=enskribo, forigo=False)
                            except bildo_model.DoesNotExist:
                                bildo = None

                            # Проверяем на итоговую заполненность визуальной информацией
                            # в результате переданных параметров редактирования:
                            # текст, картинка, видео
                            if (((not enskribo.teksto and 'teksto' not in kwargs) or kwargs.get('teksto') == '')
                                    and ((not enskribo.video and 'video' not in kwargs) or kwargs.get('video') == '')
                                    and ((not bildo and 'bildo' not in info.context.FILES) or kwargs.get(
                                        'bildo') == '')):
                                message = _('В результате редактирования отсутствует содержание записи')
                                return RedaktuMuroEnskribo(status=True, message=message)

                            update_fields = []

                            for attr, val in kwargs.items():
                                if attr == 'teksto':
                                    update_fields.append(attr)
                                    set_enhavo(enskribo.teksto, teksto=kwargs.get('teksto'),
                                               lingvo=get_lang_kodo(info.context))
                                elif attr == 'chefa_lingvo' and kwargs.get('chefa_lingvo', False):
                                    set_enhavo(enskribo.teksto, lingvo=get_lang_kodo(info.context),
                                               chefa=True)
                                elif attr == 'priskribo':
                                    set_priskribo(enskribo.teksto, teksto=kwargs.get('priskribo'),
                                                  lingvo=get_lang_kodo(info.context))
                                elif attr == 'publikigo':
                                    if not enskribo.publikigo and val:
                                        enskribo.publikiga_dato = timezone.now()
                                        setattr(enskribo, attr, val)
                                        update_fields.append(attr)
                                elif attr in model_fields:
                                    update_fields.append(attr)
                                    setattr(enskribo, attr, val)

                            if 'bildo' in info.context.FILES:
                                if bildo:
                                    bildo.forigo = True
                                    bildo.lasta_autoro = info.context.user
                                    bildo.lasta_dato = timezone.now()
                                    bildo.save()

                                bildo_params = {
                                    'bildo': info.context.FILES.get('bildo'),
                                    'posedanto': enskribo
                                }

                                if not isinstance(muro, MurojUzantoMuro):
                                    bildo_params['autoro'] = info.context.user

                                bildo_model.objects.create(**bildo_params)
                            elif kwargs.get('bildo') == '' and bildo:
                                bildo.forigo = True
                                bildo.save()

                            enskribo.lasta_autoro = info.context.user
                            enskribo.lasta_dato = timezone.now()
                            enskribo.save(update_fields=update_fields)

                            if 'forigo' in kwargs or 'arkivo' in kwargs:
                                arkivo = enskribo.arkivo or enskribo.forigo
                                komentoj = kom_model.objects.filter(posedanto=enskribo, arkivo=(not arkivo),
                                                                    forigo=False)
                                komentoj.update(arkivo=arkivo, arkiva_dato=enskribo.arkiva_dato)

                            status = True
                            message = _('Запись изменена')

                            if not lasta_statuso and enskribo.publikigo:
                                email_post_notice.delay(enskribo._meta.model_name, enskribo.uuid)

                        return RedaktuMuroEnskribo(status=status, message=message, enskribo=enskribo)

                else:
                    # 2. Создаем новое сообщение
                    if 'forigo' in kwargs:
                        message = _('Нельзя использовать параметр "forigo" при создании новой записи')
                    elif (('teksto' not in kwargs or kwargs.get('teksto') == '')
                          and 'bildo' not in info.context.FILES
                          and 'video' not in kwargs):
                        message = _(
                            'Не задан ни один из параметров ("teksto", "bildo", "video") при создании новой записи')

                    if 'aliro' in kwargs:
                        aliro = RedaktuMuroEnskribo.__get_aliro(kwargs.get('aliro'))

                        if not aliro:
                            message = _('Неверный код доступа к записи ("aliro")')

                    if 'komentado_aliro' in kwargs:
                        komentado_aliro = RedaktuMuroEnskribo.__get_aliro(kwargs.get('komentado_aliro'))

                        if not komentado_aliro:
                            message = _('Неверный код доступа к комментариеям записи ("komentado_aliro")')

                    if message:
                        return RedaktuMuroEnskribo(status=status, message=message)

                    params = {
                        'posedanto_id': komunumo_uuid,
                        'video': None,
                        'aliro': aliro,
                        'komentado_aliro': komentado_aliro,
                        'publikigo': True,
                        'publikiga_dato': timezone.now(),
                        'arkivo': False,
                        'forigo': False,
                    }

                    for param in list(params):
                        if param in kwargs:
                            if param == 'publikiga_dato':
                                params[param] = timezone.now() if params['publikigo'] else None
                            elif param == 'arkiva_dato':
                                params[param] = timezone.now() if params['arkivo'] else None
                            params[param] = kwargs.get(param)
                        elif params[param] is None:
                            del params[param]

                    if isinstance(muro, MurojUzantoMuro):
                        params['posedanto_id'] = info.context.user.id
                    else:
                        params['autoro'] = info.context.user
                        params['autoro_montri'] = kwargs.get('autoro_montri', False)

                    enskribo = enskribo_model.objects.create(muro=muro, **params)

                    if 'teksto' in kwargs and kwargs.get('teksto') != '':
                        set_enhavo(enskribo.teksto, kwargs.get('teksto'), lingvo=get_lang_kodo(info.context))
                        enskribo.save(update_fields=['teksto'])

                    if 'bildo' in info.context.FILES:
                        bildo_params = {
                            'bildo': info.context.FILES.get('bildo'),
                            'posedanto': enskribo,
                        }

                        if not isinstance(muro, MurojUzantoMuro):
                            bildo_params['autoro'] = info.context.user

                        bildo_model.objects.create(**bildo_params)

                    status = True
                    message = _('Запись успешно создана')

                    # Рассылаем почтовые уведомления
                    if (('poshto' not in kwargs or kwargs.get('poshto')) and enskribo.publikigo
                            and not isinstance(muro, MurojUzantoMuro)):
                        email_post_notice.delay(enskribo._meta.model_name, enskribo.uuid)

                    # Отправляем внутренние уведомления
                    sciigi_uzantojn.delay(enskribo._meta.model_name, enskribo.uuid)

                    return RedaktuMuroEnskribo(status=status, message=message, enskribo=enskribo)

            else:
                message = _('Сообщество не найдено')

        else:
            message = _('Требуется авторизация')

        return RedaktuMuroEnskribo(status=status, message=message)


class RedaktuMuroKomento(SiriusoAuthMutation, graphene.Mutation):
    class Arguments:
        enskribo_uuid = graphene.UUID(required=True)
        komento_uuid = graphene.UUID()
        teksto = graphene.String()
        publikigo = graphene.Boolean()
        forigo = graphene.Boolean()

    status = graphene.Boolean()
    message = graphene.String()
    komento = graphene.Field(MurojEnskriboKomento)

    @staticmethod
    def __get_komento_model(enskribo_uuid, uzanto):
        models_dict = {
            MuroEnskribo: (KomunumoMembro, MuroEnskriboKomento),
            MurojUzantoEnskribo: (None, MurojUzantoEnskriboKomento),
        }

        for enskribo_model, kom_models in models_dict.items():
            try:
                enskribo = (enskribo_model.objects.select_related('komentado_aliro')
                            .get(uuid=enskribo_uuid, forigo=False))
                membro_model = kom_models[0]
                kom_aliro = enskribo.komentado_aliro.kodo

                aliro = (membro_model.objects.filter(posedanto=enskribo.posedanto, autoro=uzanto, forigo=False)
                         if membro_model else None)

                if aliro and aliro.count():
                    aliro = aliro[0].tipo.kodo
                else:
                    aliro = None

                out = [aliro, kom_aliro]
                out.extend(kom_models[1:])
                return out
            except enskribo_model.DoesNotExist:
                pass

        return None

    @staticmethod
    def mutate(root, info, enskribo_uuid, **kwargs):
        status = False

        if info.context.user.is_authenticated:
            result = RedaktuMuroKomento.__get_komento_model(enskribo_uuid, info.context.user)

            if not result:
                message = _('Запись не найдена')
                return RedaktuMuroKomento(status=status, message=message)

            aliro, kom_aliro, kom_model = result
            has_adm_perm = aliro in ('moderiganto', 'administranto', 'komunumano-adm', 'komunumano-mod')
            has_perm = has_adm_perm or (kom_aliro == 'chiuj')

            if not has_perm:
                message = _('Недостаточно прав')
                return RedaktuMuroKomento(status=status, message=message)

            # Определяем тип операции: редактирование или создание нового комментария
            if 'komento_uuid' in kwargs:
                params = ['teksto', 'publikigo', 'forigo']

                if not len(set(params).intersection(set(kwargs.keys()))):
                    message = _('Не заданы параметры для изменения')
                elif 'teksto' in kwargs and kwargs.get('teksto') == '':
                    message = _('В результате редактирования отсутствует содержание комментария')
                else:
                    try:
                        komento = (kom_model.objects.get(
                            uuid=kwargs.get('komento_uuid'), posedanto_id=enskribo_uuid, forigo=False,
                        ))
                        # Определяем права на редактирование комментария
                        has_perm = (komento.autoro == info.context.user) or has_adm_perm

                        if has_perm:
                            for cur, val in kwargs.items():
                                if cur == 'teksto':
                                    set_enhavo(komento.teksto, val, get_lang_kodo(info.context))
                                else:
                                    setattr(komento, cur, val)

                                    if cur == 'publikigo':
                                        komento.publikiga_dato = timezone.now()
                                    elif cur == 'arkivo':
                                        komento.arkiva_dato = timezone.now()

                            komento.lasta_autoro = info.context.user
                            komento.lasta_dato = timezone.now()
                            komento.save()
                            status = True
                            message = _('Комментарий изменён')
                            return RedaktuMuroKomento(status=status, message=message, komento=komento)
                        else:
                            message = _('Недостаточно прав для редактирования комментария')
                    except kom_model.DoesNotExist:
                        message = _('Комментарий не найден')
            else:
                if 'forigo' in kwargs:
                    message = _('Нельзя использовать параметр "forigo" при создании нового комментария')
                elif 'teksto' in kwargs and kwargs.get('teksto') != '':
                    # Пока зашиваю часть параметров публикации в коде
                    # В случае реализации премодерации надо переделать
                    new_args = {
                        'publikigo': True,
                        'arkivo': False,
                        'forigo': False,
                    }

                    komento = (kom_model.objects
                               .create(posedanto_id=enskribo_uuid, autoro=info.context.user, **new_args))
                    set_enhavo(komento.teksto, kwargs.get('teksto'), get_lang_kodo(info.context))
                    komento.save()

                    status = True
                    message = _('Комментарий создан')
                    return RedaktuMuroKomento(status=status, message=message, komento=komento)
                else:
                    message = _('Не задан обязательный параметр "teksto" при создании комментария')
        else:
            message = _('Требуется авторизация')

        return RedaktuMuroKomento(status=status, message=message)


def get_enskribo_by_uuid(uuid, uzanto):
    try:
        enskribo = MuroEnskribo.objects.get(uuid=uuid, forigo=False, publikigo=True, arkivo=False)

        if not (get_enhavo(enskribo.teksto, empty_values=True)[0] or enskribo.video
                or MuroEnskriboBildo.objects.filter(
                    posedanto=enskribo, forigo=False, arkivo=False, publikigo=True
                ).count()):
            enskribo = enskribo.originalo or enskribo.originalo_uzanto or enskribo

        return enskribo
    except MuroEnskribo.DoesNotExist:
        pass

    try:
        enskribo = MurojUzantoEnskribo.objects.get(uuid=uuid, forigo=False, publikigo=True, arkivo=False)

        if not (get_enhavo(enskribo.teksto, empty_values=True)[0] or enskribo.video
                or MurojUzantoEnskriboBildo.objects.filter(
                    posedanto=enskribo, forigo=False, arkivo=False, publikigo=True
                ).count()):
            enskribo = enskribo.originalo or enskribo.originalo_uzanto or enskribo

        return enskribo
    except MurojUzantoEnskribo.DoesNotExist:
        return None


def get_celo_muro(uuid, uzanto):
    try:
        return Komunumo.objects.get(
            uuid=uuid, forigo=False, komunumoj_komunumomembro_posedanto__tipo__kodo__in=(
                'administranto', 'moderiganto', 'komunumano-adm', 'komunumano-adm'
            ),
            komunumoj_komunumomembro_posedanto__forigo=False, komunumoj_komunumomembro_posedanto__autoro=uzanto
        )
    except Komunumo.DoesNotExist:
        pass

    try:
        return Uzanto.objects.get(uuid=uuid, id=uzanto.id, is_active=True)
    except Uzanto.DoesNotExist:
        return None


class TeletransportiEnskribo(graphene.Mutation):
    status = graphene.Boolean()
    message = graphene.String()
    enskribo = graphene.Field(MuroEnskriboNode)
    uzanto_enskribo = graphene.Field(MurojUzantoEnskriboNode)
    tuta_teleportado = graphene.Int()

    class Arguments:
        enskribo_uuid = graphene.UUID(required=True)
        komunumo_uuid = graphene.UUID(required=True)
        teksto = graphene.String()

    @staticmethod
    def teleportado_statistiko(enskribo):
        # Статистика телепортаций
        rel_name = 'originalo' if isinstance(enskribo, MuroEnskribo) else 'originalo_uzanto'
        query = {
            rel_name: enskribo,
            'forigo': False,
            'arkivo': False,
            'publikigo': True
        }

        return MuroEnskribo.objects.filter(**query).count() + MurojUzantoEnskribo.objects.filter(**query).count()

    @staticmethod
    def mutate(root, info, enskribo_uuid, komunumo_uuid, **kwargs):
        status = False
        message = None
        enskribo = None
        nova_enskrobo = None
        nova_uzanto_enskribo = None
        celo_enskribo_modelo = None
        uzanto = info.context.user
        statistoko = None

        if uzanto.is_authenticated:
            enskribo = get_enskribo_by_uuid(enskribo_uuid, uzanto)

            if not enskribo:
                message = _('Телепортируемое сообщение не найдена')

            if enskribo:
                celo_kom = get_celo_muro(komunumo_uuid, uzanto)
                celo_muro = None
                kom_aliro = None

                if not celo_kom:
                    message = _('Стена назначения не найдена или нет прав для публикации на ней')
                elif celo_kom:
                    if isinstance(celo_kom, Uzanto):
                        try:
                            celo_muro = MurojUzantoMuro.objects.get(posedanto=celo_kom, forigo=False)
                            celo_enskribo_modelo = MurojUzantoEnskribo
                            kom_aliro = UzantojAliro.objects.get(kodo="chiuj")
                        except MurojUzantoMuro.DoesNotExist:
                            pass
                    else:
                        try:
                            celo_muro = Muro.objects.get(
                                posedanto=celo_kom,
                                forigo=False
                            )
                            celo_enskribo_modelo = MuroEnskribo
                            kom_aliro = KomunumojAliro.objects.get(kodo="chiuj")
                        except Muro.DoesNotExist:
                            message = _('Стена назначения не найдена или нет прав для публикацию на ней')

                if not message:
                    nova_kwargs = {
                        'muro': celo_muro,
                        'aliro': kom_aliro,
                        'komentado_aliro': kom_aliro,
                        'posedanto': celo_kom,
                        'arkivo': False,
                        'forigo': False,
                        'publikigo': True,
                        'publikiga_dato': timezone.now(),
                    }

                    if issubclass(celo_enskribo_modelo, MuroEnskribo):
                        nova_kwargs['autoro'] = uzanto

                    if isinstance(enskribo, MuroEnskribo):
                        nova_kwargs['originalo'] = enskribo
                    else:
                        nova_kwargs['originalo_uzanto'] = enskribo

                    nova_enskrobo = celo_enskribo_modelo.objects.create(**nova_kwargs)

                    if kwargs.get('teksto', False):
                        set_enhavo(nova_enskrobo.teksto, kwargs.get('teksto'), get_lang_kodo(info.context))
                        nova_enskrobo.save()

                    if not issubclass(celo_enskribo_modelo, MuroEnskribo):
                        nova_uzanto_enskribo = nova_enskrobo
                        nova_enskrobo = None

                    statistoko = TeletransportiEnskribo.teleportado_statistiko(enskribo)

                    status = True
                    message = _('Запись успешно телепортирована')

        else:
            message = _('Требуется авторизация')

        return TeletransportiEnskribo(status=status, message=message, enskribo=nova_enskrobo,
                                      uzanto_enskribo=nova_uzanto_enskribo, tuta_teleportado=statistoko)


class Mutations(graphene.ObjectType):
    interesa_enskribo = InteresaEnskribo.Field()
    redaktu_muro_enskribo = RedaktuMuroEnskribo.Field()
    redaktu_muro_enskribo_komento = RedaktuMuroKomento.Field()
    teletransporti_enskribo = TeletransportiEnskribo.Field()
